﻿namespace Neovolve.Extensibility.ReSharper
{
    using System;
    using JetBrains.Application;
    using JetBrains.ReSharper.Psi;
    using JetBrains.ReSharper.Psi.CSharp;
    using JetBrains.ReSharper.Psi.CSharp.Tree;
    using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
    using JetBrains.ReSharper.Psi.Tree;

    /// <summary>
    /// The reference expression node descriptor.
    /// </summary>
    /// <remarks>
    /// A reference expression occurs for direct clr type references.
    ///   For example <c>Int32.MaxValue</c>.
    /// </remarks>
    internal class ReferenceExpressionNodeDescriptor : NodeDescriptor
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ReferenceExpressionNodeDescriptor"/> class.
        /// </summary>
        /// <param name="node">
        /// The node.
        /// </param>
        public ReferenceExpressionNodeDescriptor(ITreeNode node)
            : base(node)
        {
        }

        /// <summary>
        /// Creates the name of the node for type.
        /// </summary>
        /// <param name="typeName">
        /// Name of the type.
        /// </param>
        /// <returns>
        /// A <see cref="ITreeNode"/> value.
        /// </returns>
        protected override ITreeNode CreateNodeForTypeName(String typeName)
        {
            IPsiModule project = Node.GetPsiModule();
            CSharpElementFactory factory = CSharpElementFactory.GetInstance(project);
            ITreeNode newNode = factory.CreateTypeUsageNode(typeName);

            if (newNode == null)
            {
                throw new InvalidOperationException("Failed to create TypeUsageNode from '" + typeName + "'.");
            }

            ITreeNode nodeCopy = Node.CopyWithResolve().ToTreeNode();
            ITreeNode childNode = nodeCopy.FirstChild as IReferenceExpressionNode;

            if (childNode == null)
            {
                childNode = nodeCopy.FirstChild as IPredefinedTypeExpressionNode;
            }

            if (childNode != null)
            {
                using (WriteLockCookie.Create())
                {
                    ModificationUtil.ReplaceChild(childNode, newNode);
                }
            }
            else
            {
                nodeCopy = newNode;
            }

            // Create a new expression with the tree copy
            String expressionText = nodeCopy.GetText();
            IReferenceExpression expression = factory.CreateReferenceExpression(expressionText);

            if (expression == null)
            {
                throw new InvalidOperationException("Failed to create reference expression for '" + expressionText + "'.");
            }

            return expression.ToTreeNode();
        }

        /// <summary>
        /// Gets the name of the node type.
        /// </summary>
        /// <returns>
        /// A <see cref="String"/> instance.
        /// </returns>
        protected override String GetNodeTypeName()
        {
            // Statements like "Int32.MaxValue" is a reference expression with "Int32"
            // being the first child reference expression
            ITreeNode childNode = Node.FirstChild;

            if (childNode != null)
            {
                if (childNode is IReferenceExpressionNode || childNode is IPredefinedTypeExpressionNode)
                {
                    return childNode.GetText();
                }
            }

            return base.GetNodeTypeName();
        }

        /// <summary>
        /// Gets a value indicating whether the node is supported by this descriptor.
        /// </summary>
        /// <value>
        /// <c>true</c> if the node is supported by this descriptor; otherwise, <c>false</c>.
        /// </value>
        public override Boolean IsSupportedNode
        {
            get
            {
                return Node is IReferenceExpressionNode;
            }
        }
    }
}